package com.yunji.product.business;

import com.google.common.collect.Lists;
import com.yunji.base.domain.BelongDO;
import com.yunji.base.enums.SwitchStatus;
import com.yunji.base.event.GuavaEventBus;
import com.yunji.base.service.AgentService;
import com.yunji.base.service.BelongService;
import com.yunji.cabinet.bo.*;
import com.yunji.cabinet.device.DeviceCommandEngine;
import com.yunji.cabinet.domain.ContainerCell;
import com.yunji.cabinet.domain.ContainerDO;
import com.yunji.cabinet.domain.CabinetTemplateCellDO;
import com.yunji.cabinet.service.ICabinetCellService;
import com.yunji.cabinet.service.ICabinetService;
import com.yunji.cabinet.service.ICabinetTemplateCellService;
import com.yunji.common.base.BaseBusiness;
import com.yunji.common.constant.LogicCode;
import com.yunji.common.enums.common.ErrorEnum;
import com.yunji.common.enums.common.UserType;
import com.yunji.common.event.notice.ChangePriceNoticeEvent;
import com.yunji.common.model.domain.AgentDO;
import com.yunji.common.model.dto.LoginInfo;
import com.yunji.common.utils.DecimalUtils;
import com.yunji.common.utils.bean.BeanUtils;
import com.yunji.order.mapper.ITradeMapper;
import com.yunji.order.service.OrderService;
import com.yunji.product.bo.*;
import com.yunji.product.consumer.ProductConsumer;
import com.yunji.product.convert.ProductConvert;
import com.yunji.product.domain.ProductAgentDO;
import com.yunji.product.domain.ProductAttrDO;
import com.yunji.product.domain.ProductDO;
import com.yunji.product.enums.GrantTypes;
import com.yunji.product.params.ProductBindParam;
import com.yunji.product.params.ProductParam;
import com.yunji.product.service.IProductAgentService;
import com.yunji.product.service.IProductAttrsService;
import com.yunji.product.service.IProductService;
import com.yunji.product.vo.ProductBindVO;
import com.yunji.product.vo.ProductVO;
import com.yunji.storage.bo.ExistsStockBO;
import com.yunji.storage.domain.StockDO;
import com.yunji.storage.service.IStockService;
import com.yunji.system.context.SystemContext;
import com.yunji.system.service.ISysConfigService;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;


/**
 * @Project: yunji-coffee
 * @Package com.yunji.product.business
 * @FileName ProductBusiness.java
 * @Copyright Copyright © 2024 云集互联. All Rights Reserved
 * @CreateTime 2024-06-10 23:38:08
 * @Description 产品业务处理
 * @Modification History
 * Author					History Time			Version			Description
 * -------------------------------------------------------------------------------
 * HuaAo0803@163.com	2024-06-10 23:38:08		1.0				1.0
 */
@Component
public class ProductBusiness extends BaseBusiness {
    @Resource
    AgentService agentService;

    @Resource
    IProductService productService;

    @Resource
    ICabinetService cabinetService;

    @Resource
    ICabinetCellService cabinetCellService;

    @Resource
    IProductAgentService productAgentService;

    @Resource
    ISysConfigService configService;

    @Resource
    IProductAttrsService productAttrsService;

    @Resource
    IStockService stockService;

    @Resource
    ICabinetTemplateCellService templateCellService;

    @Resource
    ProductConsumer productConsumer;

    @Resource
    BelongService belongService;

    @Resource
    OrderService orderService;

    @Resource
    private ITradeMapper tradeMapper;

    private static final String CHANGE_MESSAGE = "名称为{}的商品,价格已经改动,请及时查看";

    private static final String PARAMS_AUTH_IDS = "authIds";

    private static final String AGENT_MAP = "agentMap";

    private static final String SITE_MAP = "siteMap";


//    /**
//     * @Title handleStockState
//     * @Desc 处理可用库存
//     * @Date 2024-09-18 11:10:38.619
//     * @param    list
//     * @param    belongId
//     */
//    private void handleStockState(List<Product> list, Long belongId) {
//        StockParam stockParam = new StockParam();
//        stockParam.setBelongId(belongId);
//        List<StockVO> voList = stockService.search(stockParam);
//        Map<Long, BigDecimal> map = voList.stream().collect(Collectors.toMap(StockVO::getProductId, StockVO::getUsableStock, (k1, k2) -> k1));
//        list.forEach(p -> {
//            Long productId = p.getId();
//            BigDecimal nums = map.getOrDefault(productId, BigDecimal.ZERO);
//            p.setUsableStock(nums);
//        });
//    }

    /**
     * @param id 商品信息ID
     * @Title select
     * @Desc 获取商品信息详情
     * @Date 2024-06-11 14:17:07.352
     * @Return Product 商品信息详情
     */
    public ProductVO select(Long id) {
        ProductVO vo = ProductConvert.DOToVO(productService.select(id));
        Assert.isTrue(BeanUtils.isNotNull(vo), ErrorEnum.BUSINESS_512010.getText());
        // 如果不是全部授权加上授权代理商ID
        if (vo.getGrantType() == 0) {
            List<Long> list = productAgentService.selectAgentIdByProductId(id);
            Long[] array1 = list.stream().toArray(Long[]::new);
            vo.setAgentIds(array1);
        }
        ProductAttrsListQueryBO param = new ProductAttrsListQueryBO();
        param.setProductId(id);
        List<ProductAttrDO> list = productAttrsService.search(param);
        vo.setAttrValues(list);
        return vo;
    }

    /**
     * @param id     商品ID
     * @param qrcode 二维码信息
     * @Title select
     * @Desc 获取货柜对应商品信息
     * @Date 2024-06-11 20:10:04.187
     * @Return Product 商品信息
     */
    public ProductDO select(Long id, String qrcode) {
        CabinetBO bo = productConsumer.selectCabinet(CabinetSelectQueryBo.builder().qrcode(qrcode).build());
        Assert.isTrue(BeanUtils.isNotNull(bo), "未找到当前货柜信息");
        ContainerDO cabinet = bo.getCabinet();

        Long belongId = cabinet.getBelongId();
        logs.info("【货柜商品详情】货柜所属ID:{}", belongId);

        ProductListQueryBO productDTO = new ProductListQueryBO();
        productDTO.setProductId(id);
        productDTO.setBelongId(belongId);
        productDTO.setUserType(UserType.SITE.getCode());
        List<ProductDO> list = productService.searchProductPrice(productDTO);
        Assert.isTrue(BeanUtils.isNotNull(list), "未找到商品信息");

        ProductDO product = list.get(0);
        Assert.isTrue(BeanUtils.isNotNull(product.getDiscountPrice()), "找不到经理用户商品折扣价");
        return product;
    }

    /**------------------------------------------BEGIN 新增商品信息 BEGIN------------------------------------------*/
    /**
     * @param product 商品信息
     * @Title checkProduct
     * @Desc 校验商品信息
     * @Date 2024-08-28 10:26:39.208
     */
    private void checkProduct(ProductVO product) {
        String name = product.getProductName();
        logs.info("【商品信息】商品名称:{}", name);
        Assert.isTrue(BeanUtils.isNotNull(name), ErrorEnum.CHECKED_511001.getText());
        Assert.isTrue((name.length() <= 15), ErrorEnum.CHECKED_511002.getText());
        Assert.isTrue(BeanUtils.isNotNull(product.getProductHeight()), ErrorEnum.CHECKED_511030.getText());

        Assert.isTrue(BeanUtils.isNotNull(product.getSku()), ErrorEnum.CHECKED_511027.getText());
        // 图片校验
        Assert.isTrue(BeanUtils.isNotNull(product.getAlbumPics()), ErrorEnum.CHECKED_511004.getText());
        // 类型校验
        Assert.isTrue(BeanUtils.isNotNull(product.getProductType()), ErrorEnum.CHECKED_511006.getText());

        String note = product.getNote();
        logs.info("【商品信息】描述信息:{}", note);
        if (BeanUtils.isNotNull(note)) {
            Assert.isTrue((note.length() <= 25), ErrorEnum.CHECKED_511003.getText());
        }
        // 单位校验
        Assert.isTrue(BeanUtils.isNotNull(product.getUnitId()), ErrorEnum.CHECKED_511025.getText());
        // 品牌校验
        Assert.isTrue(BeanUtils.isNotNull(product.getBrandId()), ErrorEnum.CHECKED_511026.getText());
    }

    /**
     * @param product 商品信息
     * @Title checkPrice
     * @Desc 校验商品价格信息
     * @Date 2024-08-28 10:30:53.583
     */
    private void checkPrice(ProductVO product) {
        String min = configService.selectConfigByKey("sys.product.minPrice");
        String max = configService.selectConfigByKey("sys.product.maxPrice");
        logs.info("【系统配置】商品最小金额：{}", min);
        logs.info("【系统配置】商品最大金额：{}", max);
        Assert.isTrue(BeanUtils.isNotNull(min), "未找到系统配置最小金额");
        Assert.isTrue(BeanUtils.isNotNull(min), "未找到系统配置最大金额");

        BigDecimal minPrice = new BigDecimal(min);
        BigDecimal maxPrice = new BigDecimal(max);

        // 首先处理价格范围限制,限缩校验范围 减少不明确校验

        // 校验成本价
        BigDecimal costPrice = product.getCostPrice();
        logs.info("【商品信息】商品成本价：{}", costPrice);
        Assert.isTrue(BeanUtils.isNotNull(costPrice), ErrorEnum.CHECKED_511007.getText());
        Assert.isTrue(DecimalUtils.less(minPrice, costPrice), ErrorEnum.CHECKED_511024.getText());
        Assert.isTrue(DecimalUtils.less(costPrice, maxPrice), ErrorEnum.CHECKED_511010.getText());

        minPrice = costPrice;

        // 校验原价/销售价
        BigDecimal price = product.getPrice();
        logs.info("【商品信息】商品原价：{}", price);
        Assert.isTrue(BeanUtils.isNotNull(price), ErrorEnum.CHECKED_511009.getText());
        Assert.isTrue(!DecimalUtils.more(minPrice, price), ErrorEnum.CHECKED_511013.getText());
        Assert.isTrue(!DecimalUtils.more(price, maxPrice), ErrorEnum.CHECKED_511012.getText());

        maxPrice = price;

        // 校验折扣价/销售价
        BigDecimal discountPrice = product.getDiscountPrice();
        logs.info("【商品信息】商品折扣价/销售价：{}", discountPrice);
        Assert.isTrue(BeanUtils.isNotNull(discountPrice), ErrorEnum.CHECKED_511008.getText());
        Assert.isTrue(!DecimalUtils.more(minPrice, discountPrice), ErrorEnum.CHECKED_511014.getText());
        Assert.isTrue(!DecimalUtils.more(discountPrice, maxPrice), ErrorEnum.CHECKED_511015.getText());

        // 校验最小折扣价
        BigDecimal minDiscountPrice = product.getMinDiscountPrice();
        logs.info("【商品信息】最小折扣价：{}", minDiscountPrice);
        Assert.isTrue(BeanUtils.isNotNull(minDiscountPrice), ErrorEnum.CHECKED_511017.getText());
        Assert.isTrue(!DecimalUtils.more(minPrice, minDiscountPrice), ErrorEnum.CHECKED_511023.getText());
        Assert.isTrue(!DecimalUtils.more(minDiscountPrice, maxPrice), ErrorEnum.CHECKED_511022.getText());
        Assert.isTrue(!DecimalUtils.more(minDiscountPrice, discountPrice), ErrorEnum.CHECKED_511028.getText());

        minPrice = minDiscountPrice;

        // 校验最小折扣价
        BigDecimal maxDiscountPrice = product.getMaxDiscountPrice();
        logs.info("【商品信息】最大折扣价：{}", maxDiscountPrice);
        Assert.isTrue(BeanUtils.isNotNull(maxDiscountPrice), ErrorEnum.CHECKED_511016.getText());
        Assert.isTrue(!DecimalUtils.more(minPrice, maxDiscountPrice), ErrorEnum.CHECKED_511021.getText());
        Assert.isTrue(!DecimalUtils.more(maxDiscountPrice, maxPrice), ErrorEnum.CHECKED_511020.getText());
        Assert.isTrue(!DecimalUtils.more(discountPrice, maxDiscountPrice), ErrorEnum.CHECKED_511029.getText());
    }

    /**
     * @param discountPrice 折扣价格
     * @param minPriceLimit 最小价格限制
     * @param maxPriceLimit 最大价格限制
     * @Title handleProductPrice
     * @Desc 处理商品真实价格
     * @Date 2024-08-29 09:33:54.931
     * @Return BigDecimal
     */
    private BigDecimal handleProductPrice(BigDecimal discountPrice, BigDecimal minPriceLimit, BigDecimal maxPriceLimit) {
        discountPrice = DecimalUtils.less(discountPrice, minPriceLimit) ? minPriceLimit : discountPrice;
        discountPrice = DecimalUtils.more(discountPrice, maxPriceLimit) ? maxPriceLimit : discountPrice;
        logs.info("【商品信息】商品最终折扣价：{}", discountPrice);
        return discountPrice;
    }

    /**
     * @param list    商品授权
     * @param product 商品信息
     * @Title createProductAgent
     * @Desc 处理代理商品授权
     * @Date 2024-08-28 11:59:31.883
     */
    private void createProductAgent(List<ProductAgentDO> list, ProductVO product) {
        Integer grantType = product.getGrantType();
        String grantText = GrantTypes.get(grantType);
        logs.info("【商品信息】授权类型={}:{}", grantType, grantText);

        /*-----------------------------------
         * 所有授权 直接新增信息
         * 指定授权 新增判断代理是否所有
         *-----------------------------------*/
        List<AgentDO> allAgent = agentService.selectAgentIds();

        Map<Long, Long> agentMap = allAgent.stream().collect(Collectors.toMap(AgentDO::getId, AgentDO::getBelongId, (k1, k2) -> k1));
        Long[] allIds = allAgent.stream().map(AgentDO::getId).toArray(Long[]::new);


        Long[] authIds = product.getAgentIds();
        if (Objects.equals(GrantTypes.ALL_GRANT.getCode(), grantType)) {
            // 全部授权,直接复制所有代理ID
            final int len = allIds.length;
            authIds = new Long[len];
            System.arraycopy(allIds, 0, authIds, 0, len);
        }

        Assert.isTrue(BeanUtils.isNotNull(authIds), "未找到指定代理商");

        Map<Long, Integer> authMap = new HashMap<>();
        product.set(PARAMS_AUTH_IDS, authIds);
        product.set("authMap", authMap);

        List<Long> authList = Arrays.asList(authIds);
        // 处理授权代理
        for (Long agentId : allIds) {
            ProductAgentDO productAgent = new ProductAgentDO();
            Integer status = (BeanUtils.isNotNull(authList) && authList.contains(agentId)) ? LogicCode.NO : LogicCode.YES;
            logs.info("【商品信息】[代理]代理ID:{},授权状态(0:否;1:是;):{}", agentId, status);
            authMap.put(agentId, status);

            BigDecimal price = product.getDiscountPrice();
            productAgent.setDiscountPrice(price);
            productAgent.setPrice(product.getPrice());
            productAgent.setCostPrice(product.getCostPrice());
            productAgent.setAgentId(agentId);
            productAgent.setProductId(product.getId());

            productAgent.setBelongId(agentMap.get(agentId));
            productAgent.setUserType(UserType.AGENT.getCode());
            productAgent.setAgentPrice(price);
            productAgent.setStatus(status);
            productAgent.setChangeStatus(LogicCode.NO);
            productAgent.setDelFlag(LogicCode.NO);
            list.add(productAgent);
        }
    }

    /**
     * @param list    商品授权
     * @param product 商品信息
     * @Title createProductSite
     * @Desc 处理网点商品授权
     * @Date 2024-08-28 12:00:09.244
     */
    private void createProductSite(List<ProductAgentDO> list, ProductVO product) {
        Long[] authIds = product.get(PARAMS_AUTH_IDS);
        Map<Long, Integer> authMap = product.get("authMap");

        // 处理授权网点
        List<BelongDO> belongList = belongService.listByAgentIds(UserType.SITE.getCode(), Lists.newArrayList(authIds));
        if (BeanUtils.isNull(belongList)) {
            return;
        }
        for (BelongDO belong : belongList) {
            ProductAgentDO productAgent = new ProductAgentDO();

            Long agentId = belong.getAgentId();
            Integer status = authMap.get(agentId);
            logs.info("【商品信息】[网点]代理ID:{},授权状态(0:否;1:是;):{}", agentId, status);

            productAgent.setDiscountPrice(product.getDiscountPrice());
            productAgent.setPrice(product.getPrice());
            productAgent.setCostPrice(product.getCostPrice());
            productAgent.setAgentId(agentId);
            productAgent.setProductId(product.getId());

            productAgent.setBelongId(belong.getBelongId());
            productAgent.setSiteId(belong.getSiteId());
            productAgent.setUserType(UserType.SITE.getCode());
            productAgent.setStatus(status);
            productAgent.setChangeStatus(LogicCode.NO);
            productAgent.setDelFlag(LogicCode.NO);
            list.add(productAgent);
        }
    }

    /**
     * @param list    创建属性信息
     * @param product 商品信息
     * @Title handleProductAttribute
     * @Desc 处理商品属性信息
     * @Date 2024-08-28 15:05:37.064
     */
    private void handleProductAttribute(List<ProductAttrDO> list, ProductVO product) {
        List<ProductAttrDO> attributes = product.getAttrValues();
        if (BeanUtils.isNull(attributes)) {
            return;
        }
        for (ProductAttrDO attribute : attributes) {
            attribute.setProductId(product.getId());
            list.add(attribute);
        }
    }

    /**
     * @param product 商品信息
     * @Title create
     * @Desc 创建商品信息
     * @Date 2024-06-11 14:17:37.274
     */
    @Transactional
    public Long create(ProductVO product) {
        logs.info("【新增商品】商品信息:{}", product);
        logs.info("【新增商品】1、校验商品基本信息");
        checkProduct(product);

        logs.info("【新增商品】2、校验商品基本信息");
        checkPrice(product);
        product.setUnitAmount(1);

        logs.info("【新增商品】3、校验商品SKU唯一性");
        Assert.isTrue(productService.checkProductSKU(product), "当前商品Sku已存在!");

        logs.info("【新增商品】4、新增商品");
        ProductDO productDO = new ProductDO();
        BeanUtils.clone(product, productDO);
        Assert.isTrue((productService.create(productDO)), "新增失败");

        logs.info("【新增商品】5、处理商品价格信息");
        BigDecimal discountPrice = product.getDiscountPrice();
        BigDecimal minPriceLimit = product.getMinDiscountPrice();
        BigDecimal maxPriceLimit = product.getMaxDiscountPrice();
        logs.info("【新增商品】折扣价格:{},最小价格限制:{},最大价格限制:{}", discountPrice, minPriceLimit, maxPriceLimit);
        BigDecimal actualPrice = handleProductPrice(discountPrice, minPriceLimit, maxPriceLimit);
        logs.info("【新增商品】商品实际价格:{}", actualPrice);
        product.setDiscountPrice(actualPrice);
        product.setId(productDO.getId());

        List<ProductAgentDO> list = new ArrayList<>();

        List<BelongDO> belongList = belongService.getByBelongType(UserType.PLATE.getCode());
        for (BelongDO belong : belongList) {
            ProductAgentDO productAgent = new ProductAgentDO();
            BeanUtils.clone(product, productAgent);
            productAgent.setId(null);
            productAgent.setProductId(productDO.getId());
            productAgent.setUserType(UserType.PLATE.getCode());
            productAgent.setBelongId(belong.getBelongId());
            productAgent.setStatus(LogicCode.NO);
            productAgent.setChangeStatus(LogicCode.NO);
            productAgent.setDelFlag(LogicCode.NO);
            list.add(productAgent);
        }

        logs.info("【新增商品】6.1、处理商品代理授权");
        createProductAgent(list, product);
        logs.info("【新增商品】6.2、处理商品网点授权");
        createProductSite(list, product);

        logs.info("【新增商品】7、处理商品属性信息");
        List<ProductAttrDO> attributes = new ArrayList<>();
        handleProductAttribute(attributes, product);

        for (ProductAgentDO productAgent : list) {
            Assert.isTrue(productAgentService.create(productAgent), "新增失败");
        }

        for (ProductAttrDO attribute : attributes) {
            Assert.isTrue(productAttrsService.create(attribute), "新增失败");
        }

        return productDO.getId();
    }
    //------------------------------------------ENDING 新增商品信息 ENDING------------------------------------------*/


    //-------------------------------------------BEGIN 更新商品信息 BEGIN-------------------------------------------*/

    /**
     * @param product 商品信息
     * @Title handleProductAgent
     * @Desc 分离代理商和网点授权信息
     * @Date 2024-08-28 14:56:03.810
     */
    public void handleProductAgent(ProductVO product) {
        Long productId = product.getId();
        logs.info("【商品信息】商品ID:{}", productId);

        Map<Long, ProductAgentDO> agentMap = new HashMap<>();
        Map<Long, ProductAgentDO> siteMap = new HashMap<>();
        List<ProductAgentDO> list = new ArrayList<>();
        product.set(AGENT_MAP, agentMap);
        product.set(SITE_MAP, siteMap);
        product.set("product", list);

        ProductAgentListQueryBO param = new ProductAgentListQueryBO();
        param.setProductId(productId);
        List<ProductAgentDO> agentList = productAgentService.search(param);
        for (ProductAgentDO agent : agentList) {
            String userType = agent.getUserType();
            Long agentId = agent.getAgentId();
            Long siteId = agent.getSiteId();
            logs.info("【商品信息】单位类型:{}, 代理ID:{},网点ID:{}", userType, agentId, siteId);

            if (BeanUtils.equals(userType, UserType.SITE.getCode())) {
                siteMap.put(siteId, agent);
                continue;
            }

            if (BeanUtils.equals(userType, UserType.AGENT.getCode())) {
                agentMap.put(agentId, agent);
            }

            if (BeanUtils.equals(userType, UserType.PLATE.getCode())) {
                list.add(agent);
            }

        }
    }

    /**
     * @Title handleCloseProductAgent
     * @Desc 分离授权被取消的商品授权
     * @Date 2024-09-29 10:43:13.628
     */
    private void handleCloseProductAgent(Map<Long, ProductAgentDO> originalMap, Map<Long, ProductAgentDO> presentMap, List<Long> closeProductAgent, List<Long> closeBelongIds) {
        for (Map.Entry<Long, ProductAgentDO> entry : originalMap.entrySet()) {
            Long id = entry.getKey();
            ProductAgentDO originalProductAgent = entry.getValue();
            Integer originalStatus = originalProductAgent.getStatus();

            if (presentMap.containsKey(id) && Objects.equals(LogicCode.NO, originalStatus)) {
                ProductAgentDO presentProductAgent = presentMap.get(id);
                Integer presentStatus = presentProductAgent.getStatus();
                if (Objects.equals(LogicCode.YES, presentStatus)) {
                    closeProductAgent.add(id);
                    closeBelongIds.add(presentProductAgent.getBelongId());
                }

            }
        }
        logs.info("【修改商品】取消授权ID:{}", closeBelongIds);
    }

    /**
     * @param productId 商品ID
     * @param ids       信息Ids
     * @Title handleProductTemplate
     * @Desc 处理取消授权商品货柜格子
     * @Date 2024-09-29 11:27:53.200
     */
    private void handleProductTemplate(List<Long> ids, Long productId) {
        List<Long> cellIds = new ArrayList<>();

        CabinetCellListQueryBO param = new CabinetCellListQueryBO();
        param.setProductId(productId);
        for (Long id : ids) {
            param.setBelongId(id);
            List<CabinetTemplateCellDO> cellList = templateCellService.searchTemplateCells(param);
            if (!cellList.isEmpty()) {
                cellIds.addAll(cellList.stream().map(CabinetTemplateCellDO::getCellId).collect(Collectors.toList()));
            }
        }

        for (Long cellId : cellIds) {
            logs.info("【删除模板格子】格子ID:{}", cellId);
            TemplateCellResetBO cell = new TemplateCellResetBO();
            cell.setCellId(cellId);
            cell.setProductId(cellId);
            templateCellService.resetCell(cell);
        }

    }

    /**
     * @param closeSiteIds 网点IDS
     * @param productId    商品ID
     * @Title handleProductStock
     * @Desc 解除货柜中的商品占用库存
     * @Date 2024-09-29 15:21:31.225
     */
    private void handleProductStock(List<Long> closeSiteIds, Long productId) {
        List<Long> cellIds = new ArrayList<>();
        // 找到网点中有此商品的货柜
        for (Long siteId : closeSiteIds) {
            CabinetCellListQueryBO bo = new CabinetCellListQueryBO();
            bo.setManagerId(siteId);
            bo.setProductId(productId);
            bo.setStatus(LogicCode.YES);
            List<ContainerCell> cellList = cabinetCellService.searchCountCell(bo);

            BelongDO belong = belongService.getBySiteId(UserType.SITE.getCode(), siteId);
            Assert.isTrue(BeanUtils.isNotNull(belong), ErrorEnum.BUSINESS_121006.getText());
            if (Objects.equals(LogicCode.YES, belong.getStockStatus()) && !cellList.isEmpty()) {
                int size = cellList.size();
                BigDecimal quantity = new BigDecimal(Integer.toString(size));
                logs.info("【修改库存】网点ID:{},货柜格子信息:{},更改数量:{}", siteId, cellList, quantity);
                List<StockDO> stockList = new ArrayList<>();
                StockDO stock = new StockDO();
                stock.setBelongId(belong.getBelongId());
                stock.setProductId(productId);
                stock.setUsableStock(quantity);
                stock.setFrozenStock(quantity.negate());
                stock.setAllStock(BigDecimal.ZERO);
                stock.setDisableStock(BigDecimal.ZERO);
                stockList.add(stock);

                productConsumer.handleStock(stockList);

                List<Long> ids = cellList.stream().map(ContainerCell::getCellId).collect(Collectors.toList());
                cellIds.addAll(ids);

            }
        }

        // 将商品从货柜中解除占用库存
        for (Long cellId : cellIds) {
            CabinetCellUpdateBO update = new CabinetCellUpdateBO();
            update.setCellId(cellId);
            update.setStatus(LogicCode.NO);
            Assert.isTrue(cabinetCellService.update(update), "修改失败");
        }

    }

    /**
     * @param product    商品信息
     * @param createList 新增代理授权
     * @param updateList 更新代理授权
     * @Title updateProductAgent
     * @Desc 处理商品代理授权
     * @Date 2024-08-28 15:17:42.090
     */
    private void updateProductAgent(ProductVO product, List<ProductAgentDO> createList, List<ProductAgentDO> updateList) {
        Integer grantType = product.getGrantType();
        String grantText = GrantTypes.get(grantType);
        logs.info("【商品信息】代理类型={}:{}", grantType, grantText);

        List<AgentDO> allAgent = agentService.selectAgentIds();
        Long[] allIds = allAgent.stream().map(AgentDO::getId).toArray(Long[]::new);
        Long[] authIds = product.getAgentIds();
        Map<Long, Long> agentMap = allAgent.stream().collect(Collectors.toMap(AgentDO::getId, AgentDO::getBelongId, (k1, k2) -> k1));
        if (Objects.equals(grantType, GrantTypes.ALL_GRANT.getCode())) {
            final int len = allIds.length;
            authIds = new Long[len];
            System.arraycopy(allIds, 0, authIds, 0, len);
        }

        product.set(PARAMS_AUTH_IDS, authIds);
        Map<Long, ProductAgentDO> map = product.get(AGENT_MAP);
        // 授权代理处理
        for (Long agentId : authIds) {

            Long belongId = agentMap.getOrDefault(agentId, null);
            if (map.containsKey(agentId)) {
                ProductAgentDO productAgent = map.get(agentId);
                productAgent.setStatus(0);
                productAgent.setBelongId(belongId);
                logs.info("【修改授权商品信息】:{}", productAgent);
                updateList.add(productAgent);
                continue;
            }

            // 兼容V2代码
            ProductAgentDO productAgent = new ProductAgentDO();
            BeanUtils.clone(product, productAgent);
            productAgent.setId(null);
            productAgent.setProductId(product.getId());
            productAgent.setBelongId(belongId);
            // 新增的代理商需要初始价
            productAgent.setAgentPrice(product.getDiscountPrice());
            productAgent.setStatus(0);
            productAgent.setAgentId(agentId);
            productAgent.setUserType(UserType.AGENT.getCode());
            createList.add(productAgent);
        }
    }

    /**
     * @param product    商品信息
     * @param createList 新增网点授权
     * @param updateList 更新网点授权
     * @Title updateProductSite
     * @Desc 处理商品网点授权
     * @Date 2024-08-28 15:18:48.634
     */
    private void updateProductSite(ProductVO product, List<ProductAgentDO> createList, List<ProductAgentDO> updateList) {
        Long[] authIds = product.get(PARAMS_AUTH_IDS);
        if (BeanUtils.isNull(authIds)) {
            return;
        }
        List<BelongDO> belongList = belongService.listByAgentIds(UserType.SITE.getCode(), Lists.newArrayList(authIds));

        // 授权网点处理
        if (BeanUtils.isNull(belongList)) {
            return;
        }
        Map<Long, ProductAgentDO> map = product.get(SITE_MAP);
        Map<Long, BelongDO> siteInfos = new HashMap<>();
        product.set("siteInfos", siteInfos);
        for (BelongDO belong : belongList) {
            Long siteId = belong.getSiteId();
            Long belongId = belong.getBelongId();
            if (map.containsKey(siteId)) {
                ProductAgentDO productAgent = map.get(siteId);
                productAgent.setBelongId(belongId);
                productAgent.setStatus(0);
                updateList.add(productAgent);

            } else {
                ProductAgentDO productAgent = new ProductAgentDO();
                BeanUtils.clone(product, productAgent);
                productAgent.setId(null);
                productAgent.setProductId(product.getId());
                productAgent.setBelongId(belongId);
                productAgent.setStatus(0);
                productAgent.setAgentId(belong.getAgentId());
                productAgent.setSiteId(siteId);
                productAgent.setUserType(UserType.SITE.getCode());
                createList.add(productAgent);
            }
            siteInfos.put(siteId, belong);
        }
    }

    /**
     * @param origins 原始商品信息
     * @param product 当前商品信息
     * @Title checkPriceChange
     * @Desc 校验价格变化
     * @Date 2024-08-28 15:31:55.901
     * @Return boolean 比对结果
     */
    public boolean checkPriceChange(ProductDO origins, ProductVO product) {
        BigDecimal originsSalePrice = origins.getPrice();
        BigDecimal currentSalePrice = product.getPrice();
        logs.info("【修改商品】原始price:{},当前price:{}", originsSalePrice, currentSalePrice);

        BigDecimal originsCostPrice = origins.getCostPrice();
        BigDecimal currentCostPrice = product.getCostPrice();
        logs.info("【修改商品】原始costPrice:{},当前costPrice:{}", originsCostPrice, currentCostPrice);

        BigDecimal originsActualPrice = origins.getDiscountPrice();
        BigDecimal currentActualPrice = product.getDiscountPrice();
        logs.info("【修改商品】原始discountPrice:{},当前discountPrice:{}", originsActualPrice, currentActualPrice);

        BigDecimal originsMinPriceRange = origins.getMinDiscountPrice();
        BigDecimal currentMinPriceRange = product.getMinDiscountPrice();
        logs.info("【修改商品】原始minDiscountPrice:{},当前minDiscountPrice:{}", originsMinPriceRange, currentMinPriceRange);

        BigDecimal originsMaxPriceRange = origins.getMaxDiscountPrice();
        BigDecimal currentMaxPriceRange = product.getMaxDiscountPrice();
        logs.info("【修改商品】原始minDiscountPrice:{},当前minDiscountPrice:{}", originsMaxPriceRange, currentMaxPriceRange);

        return DecimalUtils.compare(originsSalePrice, currentSalePrice)
                && DecimalUtils.compare(originsCostPrice, currentCostPrice)
                && DecimalUtils.compare(originsActualPrice, currentActualPrice)
                && DecimalUtils.compare(originsMinPriceRange, currentMinPriceRange)
                && DecimalUtils.compare(originsMaxPriceRange, currentMaxPriceRange);
    }

    /**
     * @param agent        授权信息
     * @param originsPrice 原始处理价格
     * @param currentPrice 当前处理价格
     * @Title setSitePrice
     * @Desc 设置网点价格
     * @Date 2024-08-28 20:05:29.615
     * @Return BigDecimal 处理后价格
     */
    private BigDecimal setSitePrice(ProductAgentDO agent, BigDecimal originsPrice, BigDecimal currentPrice) {
        BelongDO site = agent.get("siteInfo");
        BigDecimal agentPrice = agent.getAgentPrice();
        BigDecimal sitePrice = agent.getManagerPrice();
        Integer customPrice = site.getCustomPrice();
        logs.info("【修改商品】网点自定义价格:{},代理定价:{},网点定价:{}", customPrice, agentPrice, sitePrice);
        BigDecimal discountPrice = (Objects.equals(SwitchStatus.ENABLED.getCode(), customPrice) && !DecimalUtils.compare(sitePrice)) || !DecimalUtils.compare(agentPrice) ? originsPrice : currentPrice;
        logs.info("【修改商品】网点折扣价格:{}", discountPrice);
        agent.setDiscountPrice(discountPrice);
        return discountPrice;
    }

    /**
     * @param agent        授权信息
     * @param originsPrice 原始处理价格
     * @param currentPrice 当前处理价格
     * @Title setAgentPrice
     * @Desc 设置代理价格
     * @Date 2024-08-28 20:05:29.615
     * @Return BigDecimal 处理后价格
     */
    private BigDecimal setAgentPrice(ProductAgentDO agent, BigDecimal originsPrice, BigDecimal currentPrice) {
        Integer changeStatus = agent.getChangeStatus();
        logs.info("【修改商品】代理变更状态:{}", changeStatus);
        BigDecimal discountPrice = Objects.equals(LogicCode.YES, changeStatus) ? originsPrice : currentPrice;
        logs.info("【修改商品】折扣代理价格:{}", discountPrice);
        agent.setAgentPrice(discountPrice);
        agent.setDiscountPrice(discountPrice);

        BigDecimal originsActualPrice = agent.getDiscountPrice();
        logs.info("【修改商品】原始价格:{}", originsActualPrice);
        if (!DecimalUtils.compare(discountPrice, originsActualPrice)) {
            return discountPrice;
        }
        agent.setChangeStatus(LogicCode.NO);
        return discountPrice;
    }

    /**
     * @param product    商品信息
     * @param updateList 变动信息
     * @Title handlePriceChange
     * @Desc 处理价格变动
     * @Date 2024-08-28 16:33:30.716
     */
    private void handlePriceChange(ProductVO product, List<ProductAgentDO> updateList) {
        Long productId = product.getId();
        ProductDO origins = productService.select(productId);
        logs.info("【修改商品】原商品信息:{}", origins);
        if (checkPriceChange(origins, product)) {
            return;
        }

        BigDecimal minPriceLimit = product.getMinDiscountPrice();
        BigDecimal maxPriceLimit = product.getMaxDiscountPrice();
        BigDecimal currentActualPrice = product.getDiscountPrice();
        logs.info("【修改商品】当前商品价格:{},最小价格限制:{},最大价格限制:{}", currentActualPrice, minPriceLimit, maxPriceLimit);
        BigDecimal currentDiscountPrice = handleProductPrice(currentActualPrice, minPriceLimit, maxPriceLimit);
        logs.info("【修改商品】当前处理价格:{}", currentDiscountPrice);

        Map<Long, BelongDO> map = product.get("siteInfos");
        List<Long> belongIds = new ArrayList<>();
        for (ProductAgentDO agent : updateList) {
            String userType = agent.getUserType();
            BigDecimal originsActualPrice = agent.getDiscountPrice();
            logs.info("【修改商品】授权所属类型:{}, 原始商品价格:{}", userType, originsActualPrice);
            BigDecimal originsDiscountPrice = handleProductPrice(originsActualPrice, minPriceLimit, maxPriceLimit);
            logs.info("【修改商品】原始处理价格:{}", currentDiscountPrice);

            BigDecimal discountPrice;
            if (UserType.SITE.getCode().equals(userType)) {
                Long siteId = agent.getSiteId();
                BelongDO site = map.get(siteId);
                agent.set("siteInfo", site);
                discountPrice = setSitePrice(agent, originsDiscountPrice, currentDiscountPrice);
            } else {
                discountPrice = setAgentPrice(agent, originsDiscountPrice, currentDiscountPrice);
            }
            logs.info("【修改商品】折扣价格:{}", discountPrice);
            if (Objects.equals(discountPrice, originsActualPrice)) {
                continue;
            }
            belongIds.add(agent.getBelongId());
        }
        // 通知价格变动
        GuavaEventBus.post(new ChangePriceNoticeEvent(SystemContext.getUser(),belongIds,productId));
    }

    /**
     * @param product 商品信息
     * @Title update
     * @Desc 更新商品信息
     * @Date 2024-06-11 14:17:58.526
     */
    @Transactional
    public void update(ProductVO product) {
        logs.info("【修改商品】商品信息：{}", product);
        checkPrice(product);

        Long productId = product.getId();
        logs.info("【修改商品】商品ID:{}", productId);
        Assert.isTrue(BeanUtils.isNotNull(productId), "修改失败");

        Assert.isTrue(productService.checkProductSKU(product), "当前商品Sku已存在!");

        // 授权修改前分类
        handleProductAgent(product);

        List<ProductAgentDO> createList = new ArrayList<>();
        List<ProductAgentDO> updateList = new ArrayList<>();

        updateProductAgent(product, createList, updateList);

        updateProductSite(product, createList, updateList);

        // 处理存在授权价格是否变动
        handlePriceChange(product, updateList);

        List<ProductAttrDO> attributes = new ArrayList<>();
        handleProductAttribute(attributes, product);

        // 关闭所有授权
        Assert.isTrue(productAgentService.updateByBO(ProductAgentUpdateBO.builder().productId(productId)
                .status(LogicCode.YES).changeStatus(LogicCode.NO).build()), "修改失败");

        List<ProductAgentDO> list = product.get("product");
        for (ProductAgentDO productAgent : list) {
            productAgent.setPrice(product.getPrice());
            productAgent.setCostPrice(product.getCostPrice());
            productAgent.setDiscountPrice(product.getDiscountPrice());
            updateList.add(productAgent);
        }

        for (ProductAgentDO pa : createList) {
            productAgentService.create(pa);
        }

        for (ProductAgentDO pa : updateList) {
            productAgentService.update(pa);
        }

        // 有授权变为没授权 如果 货柜中有此商品销售并有货 需要解除占用库存
        // 找到货柜中存在此商品并出售的商品
        // 找到使用该商品的模板 去除商品

        // 更改后 分类
        ProductVO present = new ProductVO();
        present.setId(productId);
        handleProductAgent(present);
        // 找出解除授权的代理和网点信息
        List<Long> closeAgentIds = new ArrayList<>();
        List<Long> closeSiteIds = new ArrayList<>();
        List<Long> closeBelongIds = new ArrayList<>();

        Map<Long, ProductAgentDO> originalAgentMap = product.get(AGENT_MAP);
        Map<Long, ProductAgentDO> originalSiteMap = product.get(SITE_MAP);
        Map<Long, ProductAgentDO> presentAgentMap = present.get(AGENT_MAP);
        Map<Long, ProductAgentDO> presentSiteMap = present.get(SITE_MAP);
        handleCloseProductAgent(originalAgentMap, presentAgentMap, closeAgentIds, closeBelongIds);
        handleCloseProductAgent(originalSiteMap, presentSiteMap, closeSiteIds, closeBelongIds);

        // 去除取消代理授权的 代理创建的模板中 有此商品的格子
        handleProductTemplate(closeBelongIds, productId);
        // 去除取消代理授权的 网点创建的模板中 有此商品的格子
        handleProductTemplate(closeSiteIds, productId);

        // 解除此商品的占用库存
        handleProductStock(closeSiteIds, productId);

        ProductDO updateDO = new ProductDO();
        BeanUtils.clone(product,updateDO);
        Assert.isTrue((productService.update(updateDO)), "修改失败");

        productAttrsService.deleteByProductIds(productId);
        for (ProductAttrDO attribute : attributes) {
            Assert.isTrue(productAttrsService.create(attribute), "新增失败");
        }
    }


    //------------------------------------------ENDING 更新商品信息 ENDING------------------------------------------*/

    /**
     * @param ids 商品信息ID数组
     * @Title delete
     * @Desc 删除商品信息
     * @Date 2024-06-11 14:18:18.013
     */
    @Transactional
    public void delete(Long... ids) {
        // FIXME 去商品明细找有没有商品引用
        Assert.isTrue(!productConsumer.existsStock(ExistsStockBO.builder().productIds(Arrays.asList(ids)).build()), "商品有库存数据");
        productAttrsService.deleteByProductIds(ids);
        productService.delete(ids);
        productAgentService.deleteByProductId(ids);
    }

    /**
     * @Title currentSearch
     * @Desc 当前用户授权商品信息列表
     * @Date 2024-06-11 16:37:12.114
     * @Return List<Product> 商品信息列表
     */
    public List<ProductDO> currentSearch() {
        LoginInfo user = SystemContext.getUser();
        Long belongId = user.getBelongId();
        String userType = user.getUserType();
        String roleType = UserType.getText(userType);
        logs.info("【商品信息】当前用户所属ID:{},用户类型={}:{}", belongId, userType, roleType);

        if (!UserType.AGENT.getCode().equals(userType)) {
            return Collections.emptyList();
        }

        // 查看是代理还是网点信息
        ProductListQueryBO dto = new ProductListQueryBO();
        dto.setBelongId(belongId);
        dto.setUserType(UserType.AGENT.getCode());
        return productService.searchProductPrice(dto);
    }

    /**
     * @param param 查询信息
     * @return List<ProductVO> 商品信息
     * @Title cabinetProduct
     * @Intro 货柜商品信息
     * @Date 2024-06-16 21:09:18.083
     */
    public List<ProductVO> cabinetProduct(ProductParam param) {
        String code = param.getQrcode();
        logs.info("【货柜商品】二维码信息:{}", code);
        CabinetBO bo = productConsumer.selectCabinet(CabinetSelectQueryBo.builder().qrcode(code).build());
        Assert.isTrue(BeanUtils.isNotNull(bo), "未找到货柜信息");
        ContainerDO cabinet = bo.getCabinet();

        String chip = cabinet.getChipImei();
        logs.info("【货柜商品】芯片信息:{}", chip);
        DeviceCommandEngine.validateOnline(chip);

        List<ContainerCell> cells = bo.getCellList();
        // 获取格子中的商品ID
        logs.info("【货柜商品】商品信息:{}", cells);
        List<Long> productIds = cells.stream().map(ContainerCell::getProductId).collect(Collectors.toList());
        Assert.isTrue(BeanUtils.isNotNull(productIds), "货柜未绑定商品信息");

        Long belongId = cabinet.getBelongId();
        logs.info("【货柜商品】所属ID：{}", belongId);
        ProductListQueryBO queryBO = new ProductListQueryBO();
        queryBO.setBelongId(belongId);
        queryBO.setProductIds(productIds);
        List<ProductDO> list = productService.searchProductPrice(queryBO);
        Assert.isTrue(BeanUtils.isNotNull(list), "未找到商品信息");
        Assert.isTrue(Objects.equals(productIds.size(), cabinet.getNums()), "商品数量和格子数不匹配");

        // 双重循环效率较低 内部处理逻辑复杂 尽量避免直接使用
        // 推荐使用map map查找使用数组+红黑树,效率较快
        Map<Long, ProductDO> map = list.stream().collect(Collectors.toMap(ProductDO::getId, product -> product, (k1, k2) -> k1));
        List<ProductVO> productList = new ArrayList<>();

        for (ContainerCell cell : cells) {
            Long productId = cell.getProductId();
            if (!map.containsKey(productId)) {
                continue;
            }
            ProductDO product = map.get(productId);
            ProductVO vo = new ProductVO();
            BeanUtils.clone(product, vo);
            vo.setStatus(cell.getStatus());
            vo.setCell(cell.getOrdinal());
            vo.setCellIsEmpty(cell.getStatus() != 0);
            vo.setUnitAmount(1);
            vo.setContainerId(cell.getContainerId());
            vo.setProductQuantity(cell.getProductQuantity());
            vo.setCeilingQuantity(cell.getCeilingQuantity());
            vo.setBelongId(cabinet.getBelongId());
            vo.setSiteId(cabinet.getSiteId());

            ProductAttrsListQueryBO attrParam = new ProductAttrsListQueryBO();
            attrParam.setProductId(productId);
            List<ProductAttrDO> attrList = productAttrsService.search(attrParam);
            vo.setAttrValues(attrList);

            Integer sales = productConsumer.countProductOrderSale(ProductOrderBO.builder().productId(productId).build());
            vo.setSale(sales);
            productList.add(vo);
        }
        return productList;
    }

    public List<ProductBindVO> getProductList(ProductBindParam param) {
        Long belongId = SystemContext.getBelongId();
        ProductListQueryBO bo = new ProductListQueryBO();
        BeanUtils.clone(param,bo);
        bo.setBelongId(belongId);
        return ProductConvert.DOToBindVOList(productService.searchProductPrice(bo));
    }

    //------------------------------------------ 搜索栏 ------------------------------------------*/

    /**
     * @param name 输入名称
     * @Title searchName
     * @Desc 商品名称匹配
     * @Date 2024-08-27 20:00:25.311
     * @Return List<String> 匹配结果
     */
    public List<String> searchName(String name) {
        Assert.isTrue((name.length() <= 30), "输入的商品名称不合规");
        ProductListQueryBO param = new ProductListQueryBO();
        param.setProductName(name);
        List<ProductDO> list = productService.searchProductPrice(param);
        List<String> response = list.stream().map(ProductDO::getProductName).distinct().collect(Collectors.toList());
        return BeanUtils.isNotNull(response) ? response : Collections.emptyList();
    }

//    public List<CabinetCellBO> getProductListById(Long cabinetId, Long belongId) {
//        List<CabinetCell> cellList = productConsumer.getCabinetCellByCabinetId(cabinetId);
//        List<CabinetCellBO> boList = new ArrayList<>();
//        for (CabinetCell cell : cellList) {
//            ProductBelongCacheDTO bo = productService.selectProductPrice(ProductSelectQueryBO.builder().productId(cell.getProductId()).belongId(belongId).build());
//            CabinetCellBO cellBO = new CabinetCellBO();
//            cellBO.setCell(cell);
//            cellBO.setProductBelongCacheDTO(bo);
//            boList.add(cellBO);
//        }
//        return boList;
//    }


}